/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2020年9月16日
 */
package com.massyframework.beanskin.web.context;

import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletContext;

import com.massyframework.beanskin.Module;
import com.massyframework.beanskin.web.ModularServletContext;

/**
 * 支持模块化ServletContext, 提供对临时目录、属性、静态资源访问的隔离
 * @author huangkh
 */
public class DefaultModularServletContext extends ServletContextWrapper
	implements ModularServletContext {

	private static final Object EMPTY = new Object();
	private final Module<?> module;
    private final ClassLoader classLoader;

    private final Map<String, String> initParams;
    private final Map<String, Object> attributes;
    
    private final ResourceLocator resourceLocator;
    

    /**
     * 构造方法
     * @param target {@link ServletContext}, ServletContext上下文
     * @param module {@link Module}, 模块
     */
    public DefaultModularServletContext(ServletContext target, Module<?> module){
        super(target);
        this.module = Objects.requireNonNull(module, "\"module\" cannot be null.");
        this.classLoader = this.module.getModuleClassLoader();
        this.attributes = new ConcurrentHashMap<>();
        this.initParams = new ConcurrentHashMap<> ();
        this.resourceLocator = new ModuleResourceLocator(module);
   
        //临时目录
        File newTmpDir = this.module.getDataFile(".tempDir").toFile();
        if (!newTmpDir.exists()) {
        	newTmpDir.mkdirs();
        }
        this.attributes.put("javax.servlet.context.tempdir", newTmpDir);
    }

    /**
     * 获取关联的模块
     *
     * @return {@link Module}
     */
    @Override
    public Module<?> getAssociatedModule() {
        return this.module;
    }

    /**
     * 根据<code>name</code>获取初始化参数
     * @param name
     * @return
     */
    @Override
    public String getInitParameter(String name) {
        if (name == null) return null;

        if (name.startsWith(ROOT_PREFIX)){
            String realName = name.substring(2);
            return this.getTargetServletContext().getInitParameter(realName);
        }
        
        String result = this.initParams.get(name);
        if (result == null){
            result = this.getTargetServletContext().getInitParameter(name);
        }
        return result;
    }
    
	/**
     * 获取所有初始化参数
     * @return
     */
    @Override
    public Enumeration<String> getInitParameterNames() {
        return Collections.enumeration(this.getInitParameterNamesToSet());
    }

    /**
     * 获取所有属性名
     *
     * @return {@link Set}
     */
    @Override
    public Set<String> getInitParameterNamesToSet() {
        Set<String> result = super.getInitParameterNamesToSet();
        result.addAll(this.initParams.keySet());
        return result;
    }

    /**
     * 设置初始化参数， 如果参数已存在，则返回<code>false</code>
     * @param name {@link String}, 参数名
     * @param value {@link String}, 参数值
     * @return {@link boolean}, 返回<code>true</code>表示设置成功，返回<code>false</code>表示设置失败
     */
    @Override
    public boolean setInitParameter(String name, String value) {
        if (name.startsWith(ROOT_PREFIX)){
            String realName = name.substring(2);
            return this.getTargetServletContext().setInitParameter(realName, value);
        }else{
            return this.initParams.putIfAbsent(name, value) == null;
        }
    }

    /**
     * 按{@code name}读取属性值
     * @param name {@link String}, 属性名
     * @return {@link Object}
     */
    @Override
    public Object getAttribute(String name) {
        return this.getAttribute(name, true);
    }
    
    @Override
	public Object getAttribute(String name, boolean useOriginal) {
    	if (name == null) return null;
        if (name.startsWith(ROOT_PREFIX)){
        	String realName = name.substring(2);
            return this.getTargetServletContext().getAttribute(realName);
        }
        if (name.startsWith(LOCAL_PREFIX)) {
        	String realName = name.substring(2);
            return this.attributes.get(realName);
        }
        
    	Object result = this.attributes.get(name);
        if (result == null && useOriginal){
            result = this.getTargetServletContext().getAttribute(name);
        }
        
        if (result == EMPTY) {
        	result = null;
        }
        return result;
	}

    /**
     * 属性名的枚举器
     * @return {@link Enumeration}
     */
    @Override
    public Enumeration<String> getAttributeNames() {
        Set<String> result = this.getAttributeNamesToSet();
        result.addAll(this.attributes.keySet());
        return Collections.enumeration(result);
    }

    /**
     * 获取所有属性名
     *
     * @return {@link Set}
     */
    @Override
    public Set<String> getAttributeNamesToSet() {
        Set<String> result = new HashSet<>(this.attributes.keySet());
        result.addAll(super.getAttributeNamesToSet());
        return result;
    }

    /**
     * 设置属性
     * @param name {@link String}, 属性名
     * @param object {@link Object}, 属性值
     */
    @Override
    public void setAttribute(String name, Object object) {
        if (name.startsWith(ROOT_PREFIX)){
            String realName = name.substring(2);
            this.getTargetServletContext().setAttribute(realName, object);
        }else{
        	if (object == null) {
        		object = EMPTY;
        	}
            this.attributes.put(name, object);
        }
    }

    /**
     * 移除属性
     * @param name {@link String}, 属性名
     */
    @Override
    public void removeAttribute(String name) {
        if (name.startsWith(ROOT_PREFIX)){
            String realName = name.substring(2);
            this.getTargetServletContext().removeAttribute(realName);
        }else{
            this.attributes.remove(name);
        }
    }

    @Override
    public InputStream getResourceAsStream(String path) {
        try {
            URL url = this.getResource(path);
            return url != null ? url.openStream() : null;
        }catch(Exception e){
            return null;
        }
    }
    
    @Override
    public Set<String> getResourcePaths(String path) {
        return this.resourceLocator.getResourcePaths(path, this.getTargetServletContext());
    }

    @Override
    public URL getResource(String path) throws MalformedURLException {
        return this.resourceLocator.getResource(path, this.getTargetServletContext());
    }

    /**
     * 类加载器
     * @return {@link ClassLoader}
     */
    @Override
    public ClassLoader getClassLoader() {
        return this.classLoader;
    }
    
    /**
     * 清除初始化参数和属性
     */
    @Override
	public void clean() {
		this.initParams.clear();
		this.attributes.clear();
	}

	@Override
    public String toString() {
        return "DefaultModularServletContext {" +
                "module=" + module +
                '}';
    }

}
